PSR-13: Link definition interfaces
PHP-FIGによって策定されたハイパーメディアのリンクの仕様。
本文
PSR-13: Link definition interfaces - PHP-FIG
PSR-13 Meta Document - PHP-FIG
RFC 5988 - Web Linkingを踏まえてPHPのインターフェイスとして定義したもので、ざっくり言うとHTMLの<a href="...">や<link rel="..." href="...">を抽象化したもの。
インターフェイス psr/link - Packagist
Psr\Link\LinkInterface
Psr\Link\EvolvableLinkInterface
Psr\Link\LinkProviderInterface
Psr\Link\EvolvableLinkProviderInterface
実装 Dependant Packages - psr/link - Packagist
fig/link-util - Packagist
PHP-FIGが提供するユーティリティクラス
symfony/web-link: The WebLink component manages links between resources. It is particularly useful to advise clients to preload and prefetch documents through HTTP and HTTP/2 pushes.
Symfonyが提供する実装
実装と仕様の問題点 https://github.com/zonuexe/Psr13-Link-PoC
まずこの仕様はシンプルなインターフェイスのみが定義されているように見えて、多くのRFC(インターネット標準)を参照している。そのうちのひとつとしてRFC 6570 - URI Templateがある。これはURI(URL)に用いられるテンプレート言語であり、URLルーティングでの変数の記述・展開に利用可能なものだ。
LinkInterface::getHref()はリンク先のURIをstringで返すと定められている。PSR-7: HTTP Message InterfacesのUriInterfaceではない。
code:php
/**
* Returns the target of the link.
*
* The target link must be one of:
* - An absolute URI, as defined by RFC 5988.
* - A relative URI, as defined by RFC 5988. The base of the relative link
* is assumed to be known based on context by the client.
* - A URI template as defined by RFC 6570.
*
* If a URI template is returned, isTemplated() MUST return True.
*
* @return string
*/
public function getHref();
内部的に持っているURIとは「絶対URI」「相対URI」そしてそれぞれがテンプレート化されたものがあり、そのどれであってもstringで返せ、と言っているのでる。このURIの素性が何者であるかを静的に判別できる要素はない。
PHP-FIGはPSRのインターフェイスとは別に実装者の便宜のためにユーティリティクラスを提供しているが、以下のようなtraitを提供している。
https://github.com/php-fig/link-util/blob/1.1.0/src/TemplatedHrefTrait.php
code:php
/**
* Determines if an href is a templated link or not.
*
* @see https://tools.ietf.org/html/rfc6570
*
* @param string $href
* The href value to check.
*
* @return bool
* True if the specified href is a templated path, False otherwise.
*/
private function hrefIsTemplated($href)
{
return strpos($href, '{') !== false ||strpos($href, '}') !== false;
}
セキュアプログラミングの原則に反するコードだ。Syntaxのあるテンプレート言語を検出するコードとは思えない乱雑さのコードであり、典型的な脆弱性の温床となりうる。とても標準化を標榜する団体が提供する実装のコードだとは思えない。
たとえば$_SERVER['REQUEST_URI']から取得した文字列を使ってLinkオブジェクトを作りたいとする。
code:php
<?php
var_dump([
'URI' => parse_url($_SERVER'REQUEST_URI', PHP_URL_PATH),
'$_GET' => $_GET,
]);
Google ChromeのようなWebブラウザから http://localhost:3939/{}?a={} にアクセスするとコマンドラインから上記のスクリプトにphp -r 'readfile("http://localhost:3939/{}?a={}");'のようなPHPスクリプトで受けるページにリクエストすると、{}はエスケープされない。
Symfony/WebLink
SymfonyではAbstractController::addLink()で利用されている。GitHub: symfony/symfony #28875 Add a new method AbstractController::addLink()
もともとはSymfonyもFig\Linkを使っていたが、この実装はコピーされる形で Symfony\Component\WebLink\Link::hrefIsTemplated()でも無批判に踏襲されている。
https://github.com/symfony/web-link/blob/v5.0.5/Link.php#L159-L162
以上のような問題があり、たとえばファイル名に { を含むようなURIをURLエンコードせずに渡すと無視されることになる。
Symfony\Component\WebLink\HttpHeaderSerializer symfony/HttpHeaderSerializer.php at v5.0.5 · symfony/symfony